package com.zhangxiaofanfan.aspect;

import cn.hutool.json.JSON;
import cn.hutool.json.JSONObject;
import com.zhangxiaofanfan.aop.OperationRecord;
import com.zhangxiaofanfan.context.UserThreadLocal;
import com.zhangxiaofanfan.db.UserData;
import com.zhangxiaofanfan.enums.OperationEntryEnum;
import com.zhangxiaofanfan.enums.OperationEnum;
import com.zhangxiaofanfan.pojo.OperationRecordEntry;
import com.zhangxiaofanfan.pojo.User;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;


/**
 * OperationRecord 注解的操作实现
 *
 * @author zhangxiaofanfan
 * @date 2023-10-23 14:35:07
 */
@Component
@Aspect
@Slf4j
public class OperationRecordAspect {

    /**
     * 声明需要实现的切点, 使用了 com.zhangxiaofanfan.aop.OperationRecord 注解的方法都被作为切点使用
     *
     * @param operationRecord   切点需要代理的注解
     */
    @Pointcut("@annotation(operationRecord)")
    public void apiRecordAnnotatedMethods(OperationRecord operationRecord) {
    }

    @Around(value = "apiRecordAnnotatedMethods(operationRecord)")
    public Object invoke(ProceedingJoinPoint joinPoint, OperationRecord operationRecord) throws Throwable {
        // 启动一个 stop watch
        StopWatch sw = new StopWatch();

        // 执行业务方法
        Object returnValue = null;

        // 获取方法名字 与 请求对象
        String methodName = joinPoint.getSignature().getName();
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes) ra;
        assert sra != null;
        HttpServletRequest request = sra.getRequest();
        // 获取代理地址、请求地址、请求类名、方法名
        User user = UserThreadLocal.get();
        OperationEnum operation = operationRecord.operation();
        OperationEntryEnum operationType = operationRecord.operationType();
        // 运行计时器
        sw.start(methodName);

        Object beforeObj = null, afterObj = null;
        if (OperationEnum.needSaveOriginRecord(operation)) {
            // 获取主键 id
            Object[] args = joinPoint.getArgs();
            Integer id = Arrays.stream(args)
                    .filter(arg -> arg instanceof Integer)
                    .findFirst()
                    .map(arg -> (int) arg)
                    .orElse(-1);
            switch (operationType) {
                case USER -> beforeObj = UserData.users.get(id);
                case MONEY -> beforeObj = UserData.moneys.get(id);
            }
        }


        try {
            // 执行代理的函数方法
            returnValue = joinPoint.proceed();
        } finally {
            // 停止计时器
            sw.stop();
            log.info("方法类型为: {}", operation);
            log.info("方法实体为: {}", operationType);

            OperationRecordEntry entry = new OperationRecordEntry();
            entry.setTrigger(user.getUser());
            entry.setOperationType(operationType.getName());
            entry.setUpdateDate(new Date());
            JSONObject diff = diffJsonDate(new JSONObject(beforeObj), new JSONObject(returnValue));
            entry.setChangeDate(diff.toString());

            switch (operation) {
                // 新增用户
                case CREATE -> {
                    switch (operationType) {
                        case USER -> {
                            log.info("新增用户");
                        }
                        case MONEY -> {
                            log.info("新增金额");
                        }
                    }
                }
                // 删除用户
                case DELETE -> {
                    switch (operationType) {
                        case USER -> {
                            log.info("删除用户");
                        }
                        case MONEY -> {
                            log.info("删除金额");

                        }
                    }
                }
                // 修改用户
                case UPDATE -> {
                    switch (operationType) {
                        case USER -> {
                            log.info("修改用户");
                        }
                        case MONEY -> {
                            log.info("修改金额");
                        }
                    }
                }
                // 查找用户
                case QUERY -> {
                    switch (operationType) {
                        case USER -> {
                            log.info("查询用户操作");
                        }
                        case MONEY -> {
                            log.info("查询金额操作");

                        }
                    }
                }
                default -> log.info("不支持的操作类型");
            }

            // 通过打印日志来模拟记录持久化过程
            log.info("修改记录为 is {}", entry);
        }
        // 返回业务方法返回值
        return returnValue;
    }


    public JSONObject diffJsonDate(JSONObject originJson, JSONObject newJson) {
        Set<String> set = new HashSet<>();
        if (originJson != null)
            set.addAll(originJson.keySet());
        if (newJson != null)
            set.addAll(newJson.keySet());
        JSONObject diffJson = new JSONObject();
        set.forEach(key -> diffJson.set(
                key,
                String.format("%s -> %s",
                        originJson == null ? "null" : originJson.getOrDefault(key, "null"),
                        newJson == null ? "null" : newJson.getOrDefault(key, "null"))
                )
        );
        return diffJson;
    }
}
